package com.falcon.parser;

import com.falcon.core.enums.ResponseCodeEnum;
import com.falcon.core.exception.BaseException;
import com.falcon.generator.vben.domain.meta.ParamInfo;
import com.falcon.domain.vo.ApiMethodMeta;
import com.falcon.mvc.domain.BasePager;
import com.swan.core.utils.PropertyUtil;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author zongf
 * @date 2024-03-16
 **/
@Slf4j
@Component
public class ControllerMethodParser {

    private DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    public ApiMethodMeta parse(Method method, boolean parseMethodParam, boolean parseMethodReturn) {
        return isApiMethod(method) ? parseMethod(method, parseMethodParam, parseMethodReturn) : null;
    }

    private ApiMethodMeta parseMethod(Method method, boolean parseMethodParam, boolean parseMethodReturn) {

        ApiMethodMeta methodMeta = new ApiMethodMeta();
        methodMeta.setName(method.getName());

        // 解析 httpMethod 和 uri
        parseHttpMethodAndUri(method, methodMeta);

        // 解析方法描述
        parseMethodDesc(method, methodMeta);

        // 即系方法返回值
        if (parseMethodReturn) {
            ParamInfo returnValue = parseMethodReturnValue(method);
            methodMeta.setReturnParam(returnValue);
        }


        if (parseMethodParam) {
            // 方法参数
            List<ParamInfo> params = parseMethodParams(method);

            // 默认为非 form 请求
            boolean isFormRequest =false;

            List<ParamInfo> requestParams = params.stream()
                    .filter(paramInfo -> !paramInfo.isPathParam()).collect(Collectors.toList());
            if (!requestParams.isEmpty()) {
                // 请求参数不包含 @RequestBody 注解，则表示为form 请求
                boolean hasRequestBody = requestParams.stream()
                        .anyMatch(ParamInfo::isHasRequestBodyAnno);
                isFormRequest = !hasRequestBody;
            }

            // 路径参数
            List<ParamInfo> pathParams = params.stream()
                    .filter(ParamInfo::isPathParam)
                    .collect(Collectors.toList());

            // 路径参数
            List<ParamInfo> methodParams = params.stream()
                    .filter(param -> !param.isPathParam())
                    .collect(Collectors.toList());

            methodMeta.setMethodParams(methodParams);
            methodMeta.setPathParams(pathParams);
            methodMeta.setFormRequest(isFormRequest);
        }

        return methodMeta;
    }

    private void parseMethodDesc(Method method, ApiMethodMeta controllerMeta) {
        if (method.isAnnotationPresent(Operation.class)) {
            Operation operation = method.getAnnotation(Operation.class);
            controllerMeta.setDescription(PropertyUtil.getOrDefault(operation::summary, ""));
        }
    }

    private boolean isApiMethod(Method method) {
        Class<? extends Annotation>[] annotationClass = new Class[]{GetMapping.class, PostMapping.class, PutMapping.class, DeleteMapping.class};

        for (Class<? extends Annotation> anno : annotationClass) {
            if (method.isAnnotationPresent(anno)) {
                return true;
            }
        }
        return false;
    }

    private void parseHttpMethodAndUri(Method method, ApiMethodMeta controllerMeta) {
        String httpMethod = "";
        String methodUri = "";
        String[] values = null;
        if (method.isAnnotationPresent(GetMapping.class)) {
            httpMethod = "Get";
            values = method.getAnnotation(GetMapping.class).value();
        } else if (method.isAnnotationPresent(PostMapping.class)) {
            httpMethod = "Post";
            values = method.getAnnotation(PostMapping.class).value();
        } else if (method.isAnnotationPresent(PutMapping.class)) {
            httpMethod = "Put";
            values = method.getAnnotation(PutMapping.class).value();
        } else if (method.isAnnotationPresent(DeleteMapping.class)) {
            httpMethod = "Delete";
            values = method.getAnnotation(DeleteMapping.class).value();
        }

        methodUri = values.length > 0 ? values[0] : "";

        StringBuilder uriSb = new StringBuilder();
        if (!methodUri.startsWith("/")) {
            uriSb.append("/");
        }
        uriSb.append(methodUri);
        controllerMeta.setRequestUri(uriSb.toString());
        controllerMeta.setHttpMethod(httpMethod);
    }


    private ParamInfo parseMethodReturnValue(Method method) {
        ParamInfo returnType = new ParamInfo();

        // 解析方法返回类型
        Type declareReturnType = method.getGenericReturnType();
        Type actualReturnType = declareReturnType;

        declareReturnType = ((ParameterizedType) declareReturnType).getActualTypeArguments()[0];

        // 如果返回参数是泛型，则解析泛型
        if (declareReturnType instanceof ParameterizedType) {

            Class wrapTypeClass = (Class) ((ParameterizedType) declareReturnType).getRawType();

            if (wrapTypeClass.isAssignableFrom(List.class)) {
                // List<T> 形式
                returnType.setType(1);
                actualReturnType = ((ParameterizedType) declareReturnType).getActualTypeArguments()[0];

                if(actualReturnType instanceof ParameterizedType){
                    // 如果参数依然是泛型
                    actualReturnType = ((ParameterizedType) actualReturnType).getActualTypeArguments()[0];
                }
            } else if (wrapTypeClass.isAssignableFrom(BasePager.class)) {
                // Pager<T> 形式
                returnType.setType(2);
                actualReturnType = ((ParameterizedType) declareReturnType).getActualTypeArguments()[0];
            } else {
                throw new BaseException(ResponseCodeEnum.UNKNOWN.getCode(), ResponseCodeEnum.UNKNOWN.getDescription());
            }
            // 真实的返回类型
            returnType.setClassType((Class) actualReturnType);
        } else {
            returnType.setClassType((Class)((ParameterizedType) actualReturnType).getActualTypeArguments()[0]);
        }

        return returnType;
    }

    private List<ParamInfo> parseMethodParams(Method method) {
        // 解析参数列表
        List<ParamInfo> paramInfos = new ArrayList<>();
        for (int i = 0; i < method.getParameters().length; i++) {
            Parameter parameter = method.getParameters()[i];

            String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);

            ParamInfo paramInfo = new ParamInfo();

            paramInfo.setName(parameterNames[i]);

            // 泛型参数
            if (parameter.getParameterizedType() instanceof ParameterizedType) {
                paramInfo.setType(1);

                Type rawType = ((ParameterizedType) parameter.getParameterizedType()).getRawType();
                Class parameterizedType = (Class)rawType;

                if (parameterizedType.isAssignableFrom(List.class)) {
                    Type actualReturnType2 = ((ParameterizedType) parameter.getParameterizedType()).getActualTypeArguments()[0];
                    paramInfo.setClassType((Class) actualReturnType2);
                    paramInfo.setType(3);
                }
            } else {
                paramInfo.setClassType(parameter.getType());
                paramInfo.setType(0);
            }

            paramInfo.setPathParam(parameter.isAnnotationPresent(PathVariable.class));
            paramInfo.setHasRequestBodyAnno(parameter.isAnnotationPresent(RequestBody.class));

            paramInfos.add(paramInfo);
        }
        return paramInfos;
    }

}
